/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ARM_MPU_H
#define ARM_MPU_H

#include "Std_Types.h"

/*****************************MPU Support****************************************/
static inline unsigned int arm_cpsr_read(void)
{
    unsigned int val;
    ASM volatile ("MRS %0, cpsr" : "=r" (val) : : "memory");
    return val;
}

#define ISB ASM volatile("ISB" : : : "memory")
#define DMB ASM volatile("DMB" : : : "memory")
#define DSB ASM volatile("DSB" : : : "memory")
#define barriers ASM volatile("" : : : "memory")

#define BITS_SHIFT(x, high, low) (((x) >> (low)) & ((1UL<<((high)-(low)+1))-1))

#define GEN_CP_REG_FUNCS(cp, reg, op1, c1, c2, op2) \
static inline unsigned int arm_read_##reg(void) { \
    unsigned int val; \
    ASM volatile("mrc " #cp ", " #op1 ", %0, " #c1 ","  #c2 "," #op2 : "=r" (val) : : "memory"); \
    return val; \
} \
\
static inline unsigned int arm_read_##reg##_relaxed(void) { \
    unsigned int val; \
    ASM("mrc " #cp ", " #op1 ", %0, " #c1 ","  #c2 "," #op2 : "=r" (val) : : "memory"); \
    return val; \
} \
\
static inline void arm_write_##reg(unsigned int val) { \
    ASM volatile("mcr " #cp ", " #op1 ", %0, " #c1 ","  #c2 "," #op2 :: "r" (val)); \
    ISB; \
} \
\
static inline void arm_write_##reg##_relaxed(unsigned int val) { \
   ASM volatile("mcr " #cp ", " #op1 ", %0, " #c1 ","  #c2 "," #op2 :: "r" (val)); \
}

#define GEN_CP15_REG_FUNCS(reg, op1, c1, c2, op2) \
    GEN_CP_REG_FUNCS(p15, reg, op1, c1, c2, op2)

#define GEN_CP14_REG_FUNCS(reg, op1, c1, c2, op2) \
    GEN_CP_REG_FUNCS(p14, reg, op1, c1, c2, op2)

/* MPU Type Register */
GEN_CP15_REG_FUNCS(mpuir, 0, c0, c0, 4)
/* System Control Register */
GEN_CP15_REG_FUNCS(sctlr, 0, c1, c0, 0)
/* Protection Region Selection Register */
GEN_CP15_REG_FUNCS(prselr, 0, c6, c2, 1)
/* Protection Region Selection Register */
GEN_CP15_REG_FUNCS(prbar, 0, c6, c3, 0)
/* Protection Region Selection Register */
GEN_CP15_REG_FUNCS(prlar, 0, c6, c3, 1)
/* Memory Attribute Indirection Registers 0 and 1 */
GEN_CP15_REG_FUNCS(mair0, 0, c10, c2, 0)
GEN_CP15_REG_FUNCS(mair1, 0, c10, c2, 1)

/* Hyp MPU Type Register */
GEN_CP15_REG_FUNCS(hmpuir, 4, c0, c0, 4)
/* Hyp MPU Region Enable Register */
GEN_CP15_REG_FUNCS(hsctlr, 4, c1, c1, 0)
/* Hyp Protection Region Selection Register */
GEN_CP15_REG_FUNCS(hprselr, 4, c6, c2, 1)
/* Hyp Protection Region Base Address Register */
GEN_CP15_REG_FUNCS(hprbar, 4, c6, c3, 1)
/* Hyp Protection Region Limit Address Register */
GEN_CP15_REG_FUNCS(hprlar, 4, c6, c3, 1)
/* Hyp Memory Attribute Indirection Register 0 and 1 */
GEN_CP15_REG_FUNCS(hmair0, 4, c10, c2, 0)
GEN_CP15_REG_FUNCS(hmair1, 4, c10, c2, 1)

typedef enum {
    /* - non-bufferable, non-cachable, shareable, RW, XN
     */
    MPU_REGION_NGNRNE,

    /* - bufferable, non-cachable, shareable, RW, XN
     */
    MPU_REGION_NGNRE,

    /*- bufferable, outer and inner Write-Through non-transient & write allocate,
     *    shareble, RW, EX
     */
    MPU_REGION_NORMAL,

    /*  - bufferable, outer and inner non-cacheable, shareble, RW,
     *    EX
     */
    MPU_REGION_NORMAL_NONCACHEABLE,

    /* read only, EX .
     */
    MPU_REGION_READONLY,

    /* read only, XN .
     */
    MPU_REGION_READONLY_XN,
    /*
        read write  XN
    */
    MPU_REGION_RW_CACHEABLE_XN,

    MPU_REGION_TYPE_MAX,
} Mpu_RegionType;

typedef struct {
    unsigned int region;
    unsigned int startAddr;
    unsigned int limitAddr;
    Mpu_RegionType type;
} Mpu_ConfigType;

void Mpu_reset(void);
void Mpu_enable(unsigned int enable);
unsigned int Mpu_Config(const Mpu_ConfigType *config, unsigned int regionNum);

/*****************************MPU Support End************************************/
#endif  /* ARM_MPU_H */
